/********************************************************************************
 * @Author: Zhenzhou Liu
 * @Date: 2024-12-28
 * @Description: Cg图论练习之最小生成树（Prim）
 *******************************************************************************/
#include <iostream>
#include <vector>
#include <queue>
#include <climits>

using namespace std;

int minimumSpanningTreePrim(vector<vector<pair<int, int>>>& graph, int m, int n) {
    priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> que;
    vector<int> dist(m, INT_MAX);
    vector<int> pred(m, -1);
    vector<bool> visited(m, false);

    dist[0] = 0;
    pred[0] = -1;
    int minDist = 0;
    que.emplace(0, 0);

    while (!que.empty()) {
        int w = que.top().first, u = que.top().second;
        que.pop();
        if (visited[u])
            continue;
        visited[u] = true;
        minDist += w;

        for (pair<int, int> edge : graph[u]) {
            int v = edge.first, w1 = edge.second;
            // w1 < dist[v]（不能在if里面） 应该和!visited[v]一起判断（原因暂不清楚）
            if (!visited[v] && w1 < dist[v]) {
                que.emplace(w1, v);
                dist[v] = w1;
                pred[v] = u;

            }
        }
    }
    for (int i = 0; i < m; ++i) {
        if (!visited[i])
            return -1;
    }

    return minDist;
}

int main() {
    int m, n;
    cin >> m >> n;
    vector<vector<pair<int, int>>> graph(m, vector<pair<int, int>>());
    int u, v, w;
    for (int i = 0; i < n; ++i) {
        cin >> u >> v >> w;
        graph[u - 1].push_back({v - 1, w});
        graph[v - 1].push_back({u - 1, w});
    }
    cout << minimumSpanningTreePrim(graph, m, n);
    return 0;
}


/* Test Case1
4 5
1 2 2
1 3 2
1 4 3
2 3 4
3 4 3
*/

/* Test Case2
9 14
1 2 4
1 7 8
2 3 8
2 7 1
3 4 7
3 5 4
3 8 2
4 5 14
4 9 9
5 6 2
5 9 10
6 7 1
6 8 4
7 8 7
*/

